<?php
/**
 * A very light framework built on webappkit kits structure
 * Use/extend this class for application/request-handler kits
 * This file is part of Webappkit, a packages system for PHP web apps
 * @link http://webappkit.net
 * @package webappkit
 * @subpackage engine
 * @author J.Ducastel <nospam0@ducastel.name>
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 */
class WakEngineKit extends WakKit {

	/**
	 * Serves http requests.
	 * Splits request URI into segments
	 * , and searches how to serve next path segment :
	 * - explicit aliases
	 * - self methods
	 * - self attributes casted as WakEngineTool
	 * - self subkits
	 * @access public
	 * @param UrlBean $uri
	 * @return bool
	 */
	public function run($response=null) {
		// building response
		try {
			if (is_null($response))
				$response=new WakEngineResponse();
			$this->response=$response;
			// pre-controller stuff
			$this->_setUp();
			// action, controller stuff
			$this->_setAction();
			$this->_runActionController();
			// rendering and sending normal response
			$this->_sendResponse();
		} catch (WakEngineErrorPage $e) {
			$this->response->error=$e;
			$this->response->view='error';
			// rendering and sending error response
			$this->_sendResponse();
		} catch (Exception $e) {
			$this->response->error=$e;
			$this->response->view='error';
			// rendering and sending error response
			$this->_sendResponse();
			// exit;
		}
		// rendering and sending response
		// $this->_sendResponse();
		// post-controller stuff
		$this->_tearDown();
	}

	/**
	 * redirects to given url
	 */
	public function _redirect($url) {
		header('location: '.$url);
		exit;
	}/**/

    /**
     * doing pre-controller stuff like authentication etc
     * @access protected
     */
    protected function _setUp() {


    }/**/

    /**
     * doing post-controller, post-response
     * @access protected
     * @return bool
     */
    protected function _tearDown() {
		// exit;
    }

	/**
	 * sets action to perform.
	 * @access protected
	 * @param string $action if null, will shift a node from response
	 * @param bool $force if true, will overwrite currently set action
	 * @return bool
	 */
	function _setAction($action=null,$force=false) {
		if (isset($this->_action) and !$force)
			return false;
		if (!$action)
			$action=$this->response->shiftNode();
			// echo "<p>$action</p>";
		$this->_action=$action;
		$this->response->view=$action;
		return true;
	}

	/**
	 * executes the appropriate action-controller for an uri node
	 * @access protected
	 * @return bool
	 */
	private function _runActionController() {
		$action=$this->_action;
		// searching for alias
		// searching for self method
		if (method_exists($this,$action)) {
            return $this->_runMethodController($action);
		}
		// searching for self tool attribute
		/*if ($r=$this->runToolController($response)) {
            return $r;
        }
		// searching for self subkit
        if ($r=$this->runSubkitController($response)) {
            return $r;
        }*/
		// searching for custom class controller
		if ($this->_searchClassController($action)) {
			return $this->_runClassController($action);
		}
		// using fall-back controller if exists
		if (method_exists($this,'_fallbackController')
		and $result=$this->_fallbackController($action)) {
			return $result;
		}
		print_r($result);
		throw new WakEngineErrorPage("No controller for $action",404);
        return false;
	}

    /**
     * runs an own method as controller to serve current node
     * @access protected
	 * @param string $method
	 * @param WakEngineResponse $response
     * @return bool
     */
    private function _runMethodController($method) {
        // getting remaining nodes
        $nodes=$this->response->getNextNodes(); // print_r($nodes);
        switch (count($nodes)) {
            case 0:
                return $this->$method();
            case 1:
                return $this->$method($nodes[0]);
            case 2:
                return $this->$method($nodes[0],$nodes[1]);
            case 3:
                return $this->$method($nodes[0],$nodes[1],$nodes[2]);
            case 4:
                return $this->$method($nodes[0],$nodes[1],$nodes[2],$nodes[3]);
            default:
                return false;
        }
    }

	/**
	 * search for a custom class controller
	 * @access private
	 * @param string $name
	 * @return string controller's class definition file's name
	 */
	private function _searchClassController($name) {
		$file=$this->getPath()
		.'controllers/'
		.$name.'.php5';
		if (!file_exists($file))
			return false;
		return $file;
	}

	/**
	 * runs a custom class controller to serve current node
	 * @access protected
	 * @param string $name controller's name
	 * @return bool
	 */
	private function _runClassController($name) {
		// including definition file
		if (!$file=$this->_searchClassController($name)) {
			throw new WakEngineErrorPage('missing controller file',500);
			return false;
		}
		include($file);
		// checking class
		$class=$name.'_Controller';
		if (!class_exists($class)) {
			throw new WakEngineErrorPage('controller class not defined',500);
		}
		$controller=new $class($this);
		return $controller->run($this->response);
	}

    /**
     * search for an own attribute controller to serve current node.
     * Will use only WakEngineTool instances
     * @access protected
	 * @param UrlBean $uri uri to serve
     * @return bool
     * /
    function runToolController($uri) {
		// getting next node to serve
        $tool=$this->node;
        if (!is_a($this->$tool,'WakEngineTool'))
            return false;
        // shortening uri
        $uri=$this->_shortenUri($uri);
        return $this->$tool->run($uri);
    }/**/

    /**
     * search for an subkit controller to serve current node
     * @access protected
	 * @param UrlBean $uri uri to serve
     * @return bool
     * /
    function runSubkitController($uri) {
        // getting next node to serve
        $id=$this->node;
        if (!in_array($id,$this->listSubkits())
        or !$kit=&$this->getSubkit($id))
            return false;
        // shortening uri
        $uri=$this->_shortenUri($uri);
        return $kit->run($uri);
    }/**/

	/**
	 * Search for view to render response, fills and sends it
	 * @todo correct http status header
	 * @access protected
	 * @param WakEngienResponse
	 * @return bool
	 */
	private function _sendResponse() {
		$view=$this->response->view;
		// searching for a template to render
		if ($this->_sendTemplate($view))
			return true;
		// searching for a view
		if ($this->_sendPHPView($view,$this->getPath().'views/'))
			return true;
		// defaulting to engine views
		$engine=Webappkit::getKit('webappkit.engine');
		if ($this->_sendPHPView($view,$engine->getPath().'views/'))
			return true;
		// trigger_error("no view for $view");
		throw new WakEngineErrorPage("no view for $view",500);
		return false;
	}

	/**
	 * search, feed and send a template for the view
	 * searches into current kit templates storage tool
	 * @access private
	 * @param string $view
	 * @return bool
	 */
	private function _sendTemplate($view) {
		// is there a valid templates storage tool ?
		if (!$this->templates or !is_a($this->templates,'WakStorage'))
			return false;
		// i18n
		$this->templates->setCriteria('lang',$this->response->lang);
		// searching template
        if (!$tpl=$this->templates->get($view) or !is_a($tpl,'DjTplTemplate')) {
            // trigger_error('no template for '.$view,E_USER_NOTICE);
            return false;
        }
		echo $tpl->render($this->response);
		return true;
	}

	/**
	 * search , feed and send a brute PHP view
	 * @access private
	 * @param string $view
	 * @param string $path must include trailing slash
	 * @return bool
	 */
	private function _sendPHPView($view,$path) {
		$file=$path.$view.'.php'; // echo "<p>$file</p>";
		if (!file_exists($file))
			return false;
		include($file);
		return true;
	}

	/**
	 * renders and send error page (404, 500 etc)
	 * @param WakEngineErrorPage $error
	 * @param return bool
	 * /
	function _errorPage($error) {
		$this->request->error=$error;
		// i18n
		$this->templates->setCriteria('lang',$this->response->lang);
		// searching template
		$id='error'.$error->getcode();
        if (!$tpl=$this->templates->get($id)) {
            trigger_error('no template for '.$id,E_USER_NOTICE);
            return false;
        }
        // echo '<pre>'; print_r($tpl);
        echo $tpl->render($this->response);
		return true;
	}/**/

	/**
	 * sets a value inside response
	 * @access protected
	 * @param string $key
	 * @param mixed $value
	 * @return bool
	 */
	protected function _setResponseElement($key, $value) {
		return $this->response->set($key,$value);
	}

	/**
	 * @var string action to perform
	 */
	private $action;

}

/**
 * Exceptions
 */
class WakEngineErrorPage extends Exception {

}
